package com.locationtracker.plus.ui.fragment.home

import android.Manifest
import android.annotation.SuppressLint
import android.content.IntentSender
import android.location.Address
import android.location.Geocoder
import android.location.Location
import android.os.Bundle
import android.os.Looper
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.blankj.utilcode.util.TimeUtils
import com.blankj.utilcode.util.ToastUtils
import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.location.*
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.tasks.Task
import com.locationtracker.plus.BuildConfig
import com.locationtracker.plus.R
import com.locationtracker.plus.app.base.BaseFragment
import com.locationtracker.plus.app.eventViewModel
import com.locationtracker.plus.app.ext.jumpBySubscribe
import com.locationtracker.plus.app.ext.showMessage
import com.locationtracker.plus.app.network.apiService
import com.locationtracker.plus.app.util.CacheUtil
import com.locationtracker.plus.data.model.bean.RouteLocation
import com.locationtracker.plus.databinding.FragmentHomeBinding
import com.locationtracker.plus.ui.dialog.PermissionRemindDialog
import com.locationtracker.plus.ui.dialog.PhoneInputDialog
import com.locationtracker.plus.viewmodel.request.RequestHomeViewModel
import com.locationtracker.plus.viewmodel.state.HomeViewModel
import com.tencent.bugly.crashreport.CrashReport
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.hgj.jetpackmvvm.ext.nav
import me.hgj.jetpackmvvm.ext.navigateAction
import me.hgj.jetpackmvvm.ext.util.notNull
import me.hgj.jetpackmvvm.ext.view.visible
import permissions.dispatcher.*
import java.io.IOException
import java.util.*

/**
 * 地图获取当前定位，有权限即可
 *
 * 获取最新定位，FusedLocationProviderClient#getCurrentLocation
 *
 * 定时获取，createLocationRequest()进行更改位置信息设置，
 * 再通过FusedLocationProviderClient#requestLocationUpdates监听
 */
@RuntimePermissions
class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>() {

    private val requestHomeViewModel by viewModels<RequestHomeViewModel>()

    private var phoneDialog: PhoneInputDialog? = null

    private lateinit var map: GoogleMap
    private lateinit var locationRequest: LocationRequest
    private lateinit var fusedLocationClient: FusedLocationProviderClient

    private var job: Job? = null
    private var lastUploadTime = 0L
    private var mLocation: Location? = null
    private val geocoder by lazy { Geocoder(mActivity, Locale.getDefault()) }
    private val locationCallback by lazy {
        object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                super.onLocationResult(locationResult)
                if (locationResult.locations.isNotEmpty()) {
                    mLocation = locationResult.locations[0]
                    mLocation?.let {
                        updateAddress()
                        map.animateCamera(
                            CameraUpdateFactory.newLatLngZoom(
                                LatLng(
                                    it.latitude,
                                    it.longitude
                                ), 15f
                            )
                        )
                    }
                }
            }
        }
    }

    override fun initView(savedInstanceState: Bundle?) {
        mDatabind.vm = mViewModel
        mDatabind.click = ProxyClick()

        (childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment).getMapAsync { p0 ->
            map = p0
            map.uiSettings.apply {
                isZoomControlsEnabled = false
                isMyLocationButtonEnabled = false
//                isScrollGesturesEnabled = false
//                isTiltGesturesEnabled = false
//                isRotateGesturesEnabled = false
            }
            CacheUtil.getLastLocation()?.let {
                map.animateCamera(
                    CameraUpdateFactory.newLatLngZoom(
                        LatLng(
                            it.coordinate.split(",")[0].toDouble(),
                            it.coordinate.split(",")[1].toDouble()
                        ), 15f
                    )
                )
            }
        }
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(mActivity)
    }

    override fun lazyLoadData() {
        //会员且没有点击过I understand 则需要弹窗
        if (CacheUtil.getForeverId().isNotEmpty() && CacheUtil.neverShowLocationDialog()) {
            //showDialog
            PermissionRemindDialog(mActivity) {
                CacheUtil.neverShowLocationDialog(false)
                if (::map.isInitialized) requestLocationWithPermissionCheck() //请求定位权限
                createLocationRequest()
                nav().jumpBySubscribe() //如果没有登陆就登陆一次
            }.let {
                it.show()
                it.window?.decorView?.background = null
            }
        } else {
            if (::map.isInitialized) requestLocationWithPermissionCheck() //请求定位权限
            createLocationRequest()
        }
    }

    override fun onDestroy() {
        fusedLocationClient.removeLocationUpdates(locationCallback)
        job?.cancel()
        job = null
        super.onDestroy()
    }

    override fun createObserver() {
        eventViewModel.run {
            requestLocation.observeInFragment(this@HomeFragment) {
                startLocationUpdatesWithPermissionCheck()
            }
            chooseContactResult.observeInFragment(this@HomeFragment) {
                phoneDialog?.let { dialog ->
                    if (dialog.isShowing) {
                        dialog.updatePhone(it)
                    } else {
                        phoneDialog = null
                    }
                }
            }
            acknowledgementDialogEvent.observeInFragment(this@HomeFragment) {
                CacheUtil.neverShowLocationDialog(true)
                //会员且没有点击过I understand 则需要弹窗
                PermissionRemindDialog(mActivity) {
                    CacheUtil.neverShowLocationDialog(false)
                    nav().jumpBySubscribe() //如果没有登陆就登陆一次
                }.let {
                    it.show()
                    it.window?.decorView?.background = null
                }
            }
        }
    }

    inner class ProxyClick {
        fun member() {
            nav().jumpBySubscribe {
                it.navigateAction(R.id.action_to_memberFragment)
            }
        }

        fun setting() {
            nav().navigateAction(R.id.action_to_mineFragment)
        }

        fun route() {
            nav().jumpBySubscribe {
                it.navigateAction(R.id.action_to_locationFragment)
            }
        }

        fun location() {
            mLocation.notNull( //本地有定位
                {
                    map.animateCamera(
                        CameraUpdateFactory.newLatLngZoom(
                            LatLng(
                                it.latitude,
                                it.longitude
                            ), 15f
                        )
                    )
                }, { //本地没有定位则显示
                    CacheUtil.getLastLocation()?.let {
                        map.animateCamera(
                            CameraUpdateFactory.newLatLngZoom(
                                LatLng(
                                    it.coordinate.split(",")[0].toDouble(),
                                    it.coordinate.split(",")[1].toDouble()
                                ), 15f
                            )
                        )
                    }
                }
            )
            if (::map.isInitialized) requestLocationWithPermissionCheck()
        }

        fun addPhone() {
            nav().jumpBySubscribe {
                PhoneInputDialog(mActivity).let {
                    phoneDialog = it
                    it.show()
                    it.window?.decorView?.background = null
                }
            }
        }
    }


    private fun createLocationRequest() {
        locationRequest = LocationRequest.create().apply {
            interval = 10000
            fastestInterval = 5000
            priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
        }
        val builder = LocationSettingsRequest.Builder()
            .addLocationRequest(locationRequest)
        val client: SettingsClient = LocationServices.getSettingsClient(mActivity)
        val task: Task<LocationSettingsResponse> = client.checkLocationSettings(builder.build())
        task.addOnSuccessListener {
            startLocationUpdatesWithPermissionCheck()
        }

        task.addOnFailureListener { exception ->
            if (exception is ResolvableApiException) {
                // Location settings are not satisfied, but this can be fixed
                // by showing the user a dialog.
                try {
                    // Show the dialog by calling startResolutionForResult(),
                    // and check the result in onActivityResult().
                    exception.startResolutionForResult(mActivity, REQUEST_CHECK_SETTINGS)
                } catch (sendEx: IntentSender.SendIntentException) {
                    // Ignore the error.
                }
            }
        }
    }

    /**
     * 获取位置持续监听
     */
    @SuppressLint("MissingPermission")
    @NeedsPermission(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )
    fun startLocationUpdates() {
        fusedLocationClient.requestLocationUpdates(
            locationRequest,
            locationCallback,
            Looper.getMainLooper()
        )
    }

    /**
     * 地图定位当前位置
     */
    @SuppressLint("MissingPermission")
    @NeedsPermission(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )
    fun requestLocation() {
        map.isMyLocationEnabled = true
    }

    /**
     * 获取最后一次定位
     */
    @SuppressLint("MissingPermission")
    @NeedsPermission(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )
    fun requestLastLocation() {
        fusedLocationClient.lastLocation.addOnSuccessListener { location: Location? ->
            showMessage(location.toString())
        }
    }

    @OnShowRationale(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )
    fun showRationaleForLocation(request: PermissionRequest) {
        AlertDialog.Builder(mActivity)
            .setPositiveButton(R.string.button_allow) { _, _ -> request.proceed() }
            .setNegativeButton(R.string.button_deny) { _, _ -> request.cancel() }
            .setCancelable(false)
            .setMessage(R.string.permission_rationale_location)
            .show()
    }

    @OnPermissionDenied(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )
    fun onLocationDenied() {
        AlertDialog.Builder(mActivity)
            .setMessage(R.string.location_permission_denied)
            .setPositiveButton(android.R.string.ok, null)
            .show()
    }

    @OnNeverAskAgain(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )
    fun onLocationNeverAskAgain() {
        ToastUtils.showShort(R.string.permission_location_never_ask_again)
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        onRequestPermissionsResult(requestCode, grantResults)
    }

    private fun updateAddress() {
        if (job?.isActive == true) return
        job = lifecycleScope.launch {
            mLocation?.let {
                try {
                    val resultList = withContext(Dispatchers.IO) {
                        geocoder.getFromLocation(it.latitude, it.longitude, 1)
                    }
                    if (resultList.isNullOrEmpty()) return@launch
                    updateAddress(it, resultList[0])
                } catch (e: IOException) {
                    CrashReport.postCatchedException(e)
                    requestHttp(it)
                }
            }
        }
    }

    private suspend fun requestHttp(location: Location) {
        try {
            val jo = withContext(Dispatchers.IO) {
                val jsonObject = apiService.googleGeocode(
                    "${location.latitude},${location.longitude}",
                    BuildConfig.MAPS_API_KEY,
                    Locale.getDefault().country
                )
                if ("OK".equals(
                        jsonObject["status"].asString,
                        ignoreCase = true
                    ) && jsonObject["results"].asJsonArray.size() > 0
                ) {
                    jsonObject["results"].asJsonArray[0].asJsonObject["formatted_address"].asString
                } else {
                    null
                }
            }
            jo?.let { address ->
                updateAddress(location, Address(Locale.getDefault()).apply {
                    setAddressLine(0, address)
                })
            }
        } catch (e: Exception) {
            CrashReport.postCatchedException(e)
        }
    }

    private fun updateAddress(location: Location, addressResponse: Address) {
        val address = addressResponse.getAddressLine(0)
        val city = addressResponse.locality
        val state = addressResponse.adminArea
        val zipCode = addressResponse.postalCode
        val country = addressResponse.countryCode
        val addStr = "$address,$city,$state,$zipCode,$country"
        val time = TimeUtils.millis2String(location.time, "yyyy-MM-dd HH:mm:ss")
        mDatabind.homeMemberLayout.visible()
        mDatabind.homeMemberTime.text = time
        mDatabind.homeMemberAddress.text = address
        CacheUtil.setLastLocation(
            RouteLocation(
                address,
                "${location.latitude},${location.longitude}",
                time
            )
        )
        if (System.currentTimeMillis() - lastUploadTime > 50 * 1000 && CacheUtil.isLogin()) {
            lastUploadTime = System.currentTimeMillis()
            requestHomeViewModel.uploadLocation(
                "${location.latitude},${location.longitude}",
                address
            )
        }
    }

    companion object {
        const val REQUEST_CHECK_SETTINGS = 99012
    }
}